Nginx 配置 HTTPS
在容器化部署中,通常使用 Nginx 作为反向代理,负责终止 TLS 并将请求转发给后端的 Node.js 应用。
基础 HTTPS 配置
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://backend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
nginx
HTTP 自动跳转 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
nginx
Nginx 配置 WSS(WebSocket Secure)
WebSocket 需要特殊的代理配置,因为它是长连接,不能使用默认的 HTTP 代理行为:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# WebSocket 代理配置
location /ws {
proxy_pass http://backend:3031;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 超时设置
proxy_read_timeout 86400s; # 24 小时
proxy_send_timeout 86400s;
}
}
nginx
关键配置项说明
| 配置项 | 说明 | 为什么需要 |
|---|---|---|
proxy_http_version 1.1 | 使用 HTTP/1.1 | WebSocket 升级需要 HTTP/1.1 |
Upgrade $http_upgrade | 传递 Upgrade 头 | 告诉后端这是 WebSocket 升级请求 |
Connection "upgrade" | 设置 Connection 头 | 防止 Nginx 关闭长连接 |
proxy_read_timeout 86400s | 读超时 24 小时 | 默认 60 秒会断开空闲的 WebSocket 连接 |
proxy_send_timeout 86400s | 写超时 24 小时 | 同上 |
最重要的一点:proxy_read_timeout 的默认值是 60 秒。如果 WebSocket 连接在 60 秒内没有任何数据传输,Nginx 会主动断开连接。这就是为什么心跳检测的 ping/pong 间隔通常设置为 30 秒——确保在 Nginx 超时之前有数据传输。如果心跳间隔设置得比较长(比如 2 分钟),就必须增大 proxy_read_timeout。
Docker Compose 集成
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
- ./certs:/etc/letsencrypt
depends_on:
- backend
restart: always
backend:
build: .
environment:
- PORT=3000
- WS_PORT=3031
restart: always
certbot:
image: certbot/certbot
volumes:
- ./certs:/etc/letsencrypt
- ./nginx/ssl:/var/www/html
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h; done'"
yaml
SSL 安全最佳实践
# ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# HSTS(强制 HTTPS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
nginx
证书更新与 Nginx 重载
在容器环境中,acme.sh 续签证书后需要重载 Nginx:
# 在 Nginx 容器中执行
docker exec nginx nginx -s reload
# 或通过 docker-compose
docker-compose exec nginx nginx -s reload
# 或通过 acme.sh 的 reloadcmd
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/key.pem \
--fullchain-file /etc/nginx/ssl/fullchain.pem \
--reloadcmd "docker exec nginx nginx -s reload"
bash
验证 HTTPS 和 WSS
# 验证 HTTPS
curl -v https://example.com
# 验证证书信息
openssl s_client -connect example.com:443 -servername example.com
# 验证 WSS(使用 wscat)
npx wscat -c wss://example.com/ws
bash
常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 502 Bad Gateway | 后端服务未启动或端口错误 | 检查后端服务状态和端口映射 |
| WebSocket 连接立即断开 | 缺少 Upgrade/Connection 头 | 检查 Nginx 的 WebSocket 配置 |
| 连接 60 秒后断开 | proxy_read_timeout 太短 | 增大超时时间或缩短心跳间隔 |
| SSL 握手失败 | 证书路径错误或过期 | 检查证书文件路径和有效期 |
| CORS 错误 | Nginx 未传递 CORS 头 | 添加 proxy_hide_header 或 add_header |
↑